using System;
using System.Collections.Generic;
using Snake.Logger;
using Snake.Singleton;
using UnityEngine;

namespace Snake
{
    public partial class App : MonoBehaviour, ISingleton
    {
        public static App Instance => SingletonMonoObjectPool<App>.Get();

        #region Blackboard

        private readonly Dictionary<string, object> _sharedBlackboard = new Dictionary<string, object>();

        public T GetBlackboard<T>(string key)
        {
            return _sharedBlackboard.TryGetValue(key, out var val) ? (T)val : default;
        }

        public void SetBlackboard<T>(string key, T value)
        {
            _sharedBlackboard[key] = value;
        }

        #endregion

        /// <summary>
        /// 程序暂停通知
        /// </summary>
        public event Action<bool> OnApplicationPauseEvent;

        partial void InitModule();

        public void OnSingletonInit()
        {
            DontDestroyOnLoad(gameObject);
            RegisterTimeUpdate("root", DelayCallUpdate);
            InitModule();
        }

        private void OnApplicationPause(bool pauseStatus)
        {
            OnApplicationPauseEvent?.Invoke(pauseStatus);
        }

        private void Update()
        {
            TimeScalingUpdate(Time.deltaTime, Time.unscaledDeltaTime);
        }

        #region Module

        private readonly Dictionary<int, Module> _modules = new Dictionary<int, Module>();

        /// <summary>
        /// 导入模块
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <exception cref="NotSupportedException"></exception>
        public void Import<T>() where T : Module, new()
        {
            var code = typeof(T).GetHashCode();
            if (_modules.ContainsKey(code))
            {
                throw new NotSupportedException("模块已存在");
            }

            var module = new T();
            module.OnEnable();
            _modules.Add(code, module);
        }

        /// <summary>
        /// 获取模块
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// <exception cref="NotSupportedException"></exception>
        public T GetModule<T>() where T : Module, new()
        {
            var code = typeof(T).GetHashCode();
            if (!_modules.TryGetValue(code, out var module))
            {
                throw new NotSupportedException("模块不存在");
            }

            return module as T;
        }

        #endregion

        #region DelayCall

        private class DelayActionContext
        {
            public float RemainingTime;
            public Action Action;
            public bool WithUnscaleTime;
        }

        private readonly List<DelayActionContext> _delayActionContexts = new List<DelayActionContext>();

        /// <summary>
        /// 主线程调用，用于各类SDK的回调
        /// </summary>
        /// <param name="action"></param>
        public void MainThreadCall(Action action)
        {
            DelayCall(0f, action);
        }

        /// <summary>
        /// 延迟调用
        /// </summary>
        /// <param name="delay"></param>
        /// <param name="action"></param>
        /// <param name="withUnscaleTime"></param>
        public void DelayCall(float delay, Action action, bool withUnscaleTime = false)
        {
            _delayActionContexts.Add(new DelayActionContext
            {
                RemainingTime = delay,
                Action = action,
                WithUnscaleTime = withUnscaleTime,
            });
        }

        private void DelayCallUpdate(float dt, float ud)
        {
            for (var i = 0; i < _delayActionContexts.Count;)
            {
                var ctx = _delayActionContexts[i];
                ctx.RemainingTime -= ctx.WithUnscaleTime ? ud : dt;
                if (ctx.RemainingTime > 0f)
                {
                    i++;
                    continue;
                }

                try
                {
                    ctx.Action.Invoke();
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }

                _delayActionContexts.RemoveAt(i);
            }
        }

        #endregion

        #region Time Scaling

        public class TimeScalingContext
        {
            /// <summary>
            /// 缩放系数
            /// </summary>
            public float ScaleRate { get; set; } = 1f;

            /// <summary>
            /// 累计流逝时间
            /// </summary>
            public float ElapseTime { get; set; }

            /// <summary>
            /// 累计流逝时间
            /// </summary>
            public float ElapseUnscaleTime { get; set; }

            private readonly Dictionary<string, TimeScalingContext> _nodes =
                new Dictionary<string, TimeScalingContext>();

            private readonly List<Action<float, float>> _add = new List<Action<float, float>>();
            private readonly List<Action<float, float>> _list = new List<Action<float, float>>();
            private readonly List<Action<float, float>> _remove = new List<Action<float, float>>();

            public TimeScalingContext this[string key]
            {
                get
                {
                    if (_nodes.TryGetValue(key, out var node))
                    {
                        return node;
                    }

                    node = new TimeScalingContext();
                    _nodes.Add(key, node);
                    return node;
                }
            }

            public void Register(Action<float, float> action)
            {
                _add.Add(action);
            }

            public void Unregister(Action<float, float> action)
            {
                _remove.Add(action);
            }

            public void OnUpdate(float dt, float ud)
            {
                dt *= ScaleRate;
                ud *= ScaleRate;
                ElapseTime += dt;
                ElapseUnscaleTime += ud;

                foreach (var action in _add)
                {
                    _list.Add(action);
                }

                _add.Clear();

                foreach (var action in _list)
                {
                    action?.Invoke(dt, ud);
                }

                foreach (var action in _remove)
                {
                    _list.Remove(action);
                }

                _remove.Clear();

                foreach (var ctx in _nodes.Values)
                {
                    ctx.OnUpdate(dt, ud);
                }
            }
        }

        private readonly TimeScalingContext _rootTimeScaling = new TimeScalingContext();

        public void RegisterTimeUpdate(string path, Action<float, float> action)
        {
            GetTimeScalingContext(path).Register(action);
        }

        public void UnregisterTimeUpdate(string path, Action<float, float> action)
        {
            GetTimeScalingContext(path).Unregister(action);
        }

        /// <summary>
        /// 从Unity默认Update驱动时间缩放管理器的更新
        /// </summary>
        /// <param name="timeDelta"></param>
        /// <param name="unscaleDelta"></param>
        private void TimeScalingUpdate(float timeDelta, float unscaleDelta)
        {
            _rootTimeScaling.OnUpdate(timeDelta, unscaleDelta);
        }

        public TimeScalingContext GetTimeScalingContext(string path)
        {
            if (path.Equals("root"))
            {
                return _rootTimeScaling;
            }

            var c = _rootTimeScaling;
            var nodes = path.Split('/');
            if (nodes[0].Equals("root"))
            {
                c = _rootTimeScaling;
                nodes = nodes[1..];
            }

            foreach (var s in nodes)
            {
                c = c[s];
            }

            return c;
        }

        /// <summary>
        /// 设置Unity时间缩放系数，这将影响所有默认Update的deltaTime，包括Unity的动画系统、粒子系统等
        /// </summary>
        /// <param name="scale"></param>
        public void SetUnityTimeScale(float scale)
        {
            Time.timeScale = scale;
        }

        /// <summary>
        /// 获取Unity时间缩放系数
        /// </summary>
        /// <returns></returns>
        public float GetUnityTimeScale()
        {
            return Time.timeScale;
        }

        /// <summary>
        /// 设置时间缩放系数
        /// </summary>
        /// <param name="path"></param>
        /// <param name="scale"></param>
        public void SetTimeScale(string path, float scale)
        {
            GetTimeScalingContext(path).ScaleRate = scale;
        }

        /// <summary>
        /// 获取时间缩放系数
        /// </summary>
        /// <param name="path"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        public float GetTimeScale(string path, float scale)
        {
            return GetTimeScalingContext(path).ScaleRate;
        }

        #endregion
    }
}